/*     */ package com.zimbra.cs.redolog;
/*     */ 
/*     */ import com.zimbra.common.util.FileUtil;
/*     */ import com.zimbra.common.util.Log;
/*     */ import com.zimbra.common.util.Pair;
/*     */ import com.zimbra.common.util.ZimbraLog;
/*     */ import com.zimbra.cs.db.Db;
/*     */ import com.zimbra.cs.mailbox.MailServiceException;
/*     */ import com.zimbra.cs.redolog.logger.FileHeader;
/*     */ import com.zimbra.cs.redolog.logger.FileLogReader;
/*     */ import com.zimbra.cs.redolog.logger.FileLogWriter;
/*     */ import com.zimbra.cs.redolog.logger.LogWriter;
/*     */ import com.zimbra.cs.redolog.op.AbortTxn;
/*     */ import com.zimbra.cs.redolog.op.Checkpoint;
/*     */ import com.zimbra.cs.redolog.op.CommitTxn;
/*     */ import com.zimbra.cs.redolog.op.RedoableOp;
/*     */ import com.zimbra.cs.util.Zimbra;
/*     */ import com.zimbra.znative.IO;
/*     */ import java.io.File;
/*     */ import java.io.IOException;
/*     */ import java.util.ArrayList;
/*     */ import java.util.HashSet;
/*     */ import java.util.Iterator;
/*     */ import java.util.LinkedHashMap;
/*     */ import java.util.LinkedHashSet;
/*     */ import java.util.List;
/*     */ import java.util.Map.Entry;
/*     */ import java.util.Set;
/*     */ import java.util.concurrent.locks.ReentrantReadWriteLock;
/*     */ import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
/*     */ import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ public class RedoLogManager
/*     */ {
/*     */   private boolean mEnabled;
/*     */   private boolean mInCrashRecovery;
/*     */   
/*     */   private static class TxnIdGenerator
/*     */   {
/*     */     private int mTime;
/*     */     private int mCounter;
/*     */     
/*     */     public TxnIdGenerator()
/*     */     {
/*  60 */       init();
/*     */     }
/*     */     
/*     */     private void init() {
/*  64 */       this.mTime = ((int)(System.currentTimeMillis() / 1000L));
/*  65 */       this.mCounter = 1;
/*     */     }
/*     */     
/*     */     public synchronized TransactionId getNext() {
/*  69 */       TransactionId tid = new TransactionId(this.mTime, this.mCounter);
/*  70 */       if (this.mCounter < 2147483647L) {
/*  71 */         this.mCounter += 1;
/*     */       } else
/*  73 */         init();
/*  74 */       return tid;
/*     */     }
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*  80 */   private final Object mInCrashRecoveryGuard = new Object();
/*     */   private boolean mShuttingDown;
/*  82 */   private final Object mShuttingDownGuard = new Object();
/*     */   
/*     */   private boolean mInPostStartupCrashRecovery;
/*     */   
/*     */   private boolean mSupportsCrashRecovery;
/*     */   
/*     */   private boolean mRecoveryMode;
/*     */   
/*     */   private File mArchiveDir;
/*     */   
/*     */   private File mLogFile;
/*     */   
/*     */   private ReentrantReadWriteLock mRWLock;
/*     */   
/*     */   private LinkedHashMap<TransactionId, RedoableOp> mActiveOps;
/*     */   
/*     */   private long mLogRolloverMinAgeMillis;
/*     */   
/*     */   private long mLogRolloverSoftMaxBytes;
/*     */   
/*     */   private long mLogRolloverHardMaxBytes;
/*     */   
/*     */   private TxnIdGenerator mTxnIdGenerator;
/*     */   
/*     */   private RolloverManager mRolloverMgr;
/*     */   
/*     */   private long mInitialLogSize;
/*     */   
/*     */   private LogWriter mLogWriter;
/*     */   
/*     */   private Object mStatGuard;
/*     */   
/*     */   private long mElapsed;
/*     */   private int mCounter;
/*     */   
/*     */   public RedoLogManager(File redolog, File archdir, boolean supportsCrashRecovery)
/*     */   {
/* 119 */     this.mEnabled = false;
/* 120 */     this.mShuttingDown = false;
/* 121 */     this.mRecoveryMode = false;
/* 122 */     this.mSupportsCrashRecovery = supportsCrashRecovery;
/*     */     
/* 124 */     this.mLogFile = redolog;
/* 125 */     this.mArchiveDir = archdir;
/*     */     
/* 127 */     this.mRWLock = new ReentrantReadWriteLock();
/* 128 */     this.mActiveOps = new LinkedHashMap(100);
/* 129 */     this.mTxnIdGenerator = new TxnIdGenerator();
/* 130 */     long minAge = RedoConfig.redoLogRolloverMinFileAge() * 60L * 1000L;
/* 131 */     long softMax = RedoConfig.redoLogRolloverFileSizeKB() * 1024L;
/* 132 */     long hardMax = RedoConfig.redoLogRolloverHardMaxFileSizeKB() * 1024L;
/* 133 */     setRolloverLimits(minAge, softMax, hardMax);
/* 134 */     this.mRolloverMgr = new RolloverManager(this, this.mLogFile);
/* 135 */     this.mLogWriter = null;
/*     */     
/* 137 */     this.mStatGuard = new Object();
/* 138 */     this.mElapsed = 0L;
/* 139 */     this.mCounter = 0;
/*     */   }
/*     */   
/*     */   protected LogWriter getLogWriter() {
/* 143 */     return this.mLogWriter;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */   public File getLogFile()
/*     */   {
/* 151 */     return this.mLogFile;
/*     */   }
/*     */   
/*     */   public File getArchiveDir() {
/* 155 */     return this.mArchiveDir;
/*     */   }
/*     */   
/*     */   public File getRolloverDestDir() {
/* 159 */     return this.mArchiveDir;
/*     */   }
/*     */   
/*     */   public LogWriter getCurrentLogWriter() {
/* 163 */     return this.mLogWriter;
/*     */   }
/*     */   
/*     */ 
/*     */   public LogWriter createLogWriter(RedoLogManager redoMgr, File logfile, long fsyncIntervalMS)
/*     */   {
/* 169 */     return new FileLogWriter(redoMgr, logfile, fsyncIntervalMS);
/*     */   }
/*     */   
/*     */   private void setInCrashRecovery(boolean b) {
/* 173 */     synchronized (this.mInCrashRecoveryGuard) {
/* 174 */       this.mInCrashRecovery = b;
/*     */     }
/*     */   }
/*     */   
/*     */   /* Error */
/*     */   public boolean getInCrashRecovery()
/*     */   {
/*     */     // Byte code:
/*     */     //   0: aload_0
/*     */     //   1: getfield 6	com/zimbra/cs/redolog/RedoLogManager:mInCrashRecoveryGuard	Ljava/lang/Object;
/*     */     //   4: dup
/*     */     //   5: astore_1
/*     */     //   6: monitorenter
/*     */     //   7: aload_0
/*     */     //   8: getfield 40	com/zimbra/cs/redolog/RedoLogManager:mInCrashRecovery	Z
/*     */     //   11: aload_1
/*     */     //   12: monitorexit
/*     */     //   13: ireturn
/*     */     //   14: astore_2
/*     */     //   15: aload_1
/*     */     //   16: monitorexit
/*     */     //   17: aload_2
/*     */     //   18: athrow
/*     */     // Line number table:
/*     */     //   Java source line #179	-> byte code offset #0
/*     */     //   Java source line #180	-> byte code offset #7
/*     */     //   Java source line #181	-> byte code offset #14
/*     */     // Local variable table:
/*     */     //   start	length	slot	name	signature
/*     */     //   0	19	0	this	RedoLogManager
/*     */     //   5	11	1	Ljava/lang/Object;	Object
/*     */     //   14	4	2	localObject1	Object
/*     */     // Exception table:
/*     */     //   from	to	target	type
/*     */     //   7	13	14	finally
/*     */     //   14	17	14	finally
/*     */   }
/*     */   
/*     */   public synchronized void start()
/*     */   {
/* 185 */     this.mEnabled = true;
/*     */     try
/*     */     {
/* 188 */       File logdir = this.mLogFile.getParentFile();
/* 189 */       if ((!logdir.exists()) && 
/* 190 */         (!logdir.mkdirs())) {
/* 191 */         throw new IOException("Unable to create directory " + logdir.getAbsolutePath());
/*     */       }
/* 193 */       if ((!this.mArchiveDir.exists()) && 
/* 194 */         (!this.mArchiveDir.mkdirs())) {
/* 195 */         throw new IOException("Unable to create directory " + this.mArchiveDir.getAbsolutePath());
/*     */       }
/*     */     } catch (IOException e) {
/* 198 */       signalFatalError(e);
/*     */     }
/*     */     
/* 201 */     setInCrashRecovery(true);
/*     */     
/*     */ 
/*     */     try
/*     */     {
/* 206 */       this.mRolloverMgr.crashRecovery();
/*     */     } catch (IOException e) {
/* 208 */       ZimbraLog.redolog.fatal("Exception during crash recovery");
/* 209 */       signalFatalError(e);
/*     */     }
/*     */     
/* 212 */     long fsyncInterval = RedoConfig.redoLogFsyncIntervalMS();
/* 213 */     this.mLogWriter = createLogWriter(this, this.mLogFile, fsyncInterval);
/*     */     
/* 215 */     ArrayList<RedoableOp> postStartupRecoveryOps = new ArrayList(100);
/* 216 */     int numRecoveredOps = 0;
/* 217 */     if (this.mSupportsCrashRecovery) {
/* 218 */       this.mRecoveryMode = true;
/* 219 */       ZimbraLog.redolog.info("Starting pre-startup crash recovery");
/*     */       try
/*     */       {
/* 222 */         this.mLogWriter.open();
/* 223 */         this.mRolloverMgr.initSequence(this.mLogWriter.getSequence());
/* 224 */         RedoPlayer redoPlayer = new RedoPlayer(true);
/*     */         try {
/* 226 */           numRecoveredOps = redoPlayer.runCrashRecovery(this, postStartupRecoveryOps);
/*     */         } finally {
/* 228 */           redoPlayer.shutdown();
/*     */         }
/* 230 */         this.mLogWriter.close();
/*     */       } catch (Exception e) {
/* 232 */         ZimbraLog.redolog.fatal("Exception during crash recovery");
/* 233 */         signalFatalError(e);
/*     */       }
/* 235 */       ZimbraLog.redolog.info("Finished pre-startup crash recovery");
/* 236 */       this.mRecoveryMode = false;
/*     */     }
/*     */     
/* 239 */     setInCrashRecovery(false);
/*     */     
/*     */     try
/*     */     {
/* 243 */       this.mLogWriter.open();
/* 244 */       this.mRolloverMgr.initSequence(this.mLogWriter.getSequence());
/* 245 */       this.mInitialLogSize = this.mLogWriter.getSize();
/*     */     } catch (IOException e) {
/* 247 */       ZimbraLog.redolog.fatal("Unable to open redo log");
/* 248 */       signalFatalError(e);
/*     */     }
/*     */     
/* 251 */     if (numRecoveredOps > 0)
/*     */     {
/*     */ 
/* 254 */       if (postStartupRecoveryOps.size() > 0) { Iterator iter;
/* 255 */         synchronized (this.mActiveOps) {
/* 256 */           for (iter = postStartupRecoveryOps.iterator(); iter.hasNext();) {
/* 257 */             RedoableOp op = (RedoableOp)iter.next();
/* 258 */             assert (op.isStartMarker());
/* 259 */             this.mActiveOps.put(op.getTransactionId(), op);
/*     */           }
/*     */         }
/*     */       }
/*     */       
/*     */ 
/* 265 */       forceRollover();
/*     */       
/*     */ 
/*     */ 
/*     */ 
/* 270 */       if (postStartupRecoveryOps.size() > 0) {
/* 271 */         synchronized (this.mShuttingDownGuard) {
/* 272 */           this.mInPostStartupCrashRecovery = true;
/*     */         }
/* 274 */         Thread psrThread = new PostStartupCrashRecoveryThread(postStartupRecoveryOps, null);
/*     */         
/* 276 */         psrThread.start();
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   private class PostStartupCrashRecoveryThread extends Thread {
/*     */     List mOps;
/*     */     
/*     */     private PostStartupCrashRecoveryThread(List ops) {
/* 285 */       super();
/* 286 */       setDaemon(true);
/* 287 */       this.mOps = ops;
/*     */     }
/*     */     
/*     */     public void run() {
/* 291 */       ZimbraLog.redolog.info("Starting post-startup crash recovery");
/* 292 */       boolean interrupted = false;
/* 293 */       for (Iterator iter = this.mOps.iterator(); iter.hasNext();) {
/* 294 */         synchronized (RedoLogManager.this.mShuttingDownGuard) {
/* 295 */           if (RedoLogManager.this.mShuttingDown) {
/* 296 */             interrupted = true;
/* 297 */             break;
/*     */           }
/*     */         }
/* 300 */         RedoableOp op = (RedoableOp)iter.next();
/*     */         try {
/* 302 */           if (ZimbraLog.redolog.isDebugEnabled())
/* 303 */             ZimbraLog.redolog.debug("REDOING: " + op);
/* 304 */           op.redo();
/*     */         }
/*     */         catch (Exception e)
/*     */         {
/*     */           AbortTxn abort;
/* 309 */           ZimbraLog.redolog.error("Redo failed for [" + op + "]." + "  Backend state of affected item is indeterminate." + "  Marking operation as aborted and moving on.", e);
/*     */         }
/*     */         finally
/*     */         {
/*     */           AbortTxn abort;
/*     */           
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/* 322 */           AbortTxn abort = new AbortTxn(op);
/* 323 */           RedoLogManager.this.logOnly(abort, true);
/*     */         }
/*     */       }
/*     */       
/* 327 */       if (!interrupted) {
/* 328 */         ZimbraLog.redolog.info("Finished post-startup crash recovery");
/*     */       }
/*     */       
/* 331 */       this.mOps.clear();
/* 332 */       this.mOps = null;
/*     */       
/* 334 */       synchronized (RedoLogManager.this.mShuttingDownGuard) {
/* 335 */         RedoLogManager.this.mInPostStartupCrashRecovery = false;
/* 336 */         if (RedoLogManager.this.mShuttingDown)
/* 337 */           RedoLogManager.this.mShuttingDownGuard.notifyAll();
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   public synchronized void stop() {
/* 343 */     if (!this.mEnabled) {
/* 344 */       return;
/*     */     }
/* 346 */     synchronized (this.mShuttingDownGuard) {
/* 347 */       this.mShuttingDown = true;
/* 348 */       if (this.mInPostStartupCrashRecovery) {
/*     */         try
/*     */         {
/* 351 */           this.mShuttingDownGuard.wait();
/*     */         }
/*     */         catch (InterruptedException e) {}
/*     */       }
/*     */     }
/*     */     try {
/* 357 */       forceRollover();
/* 358 */       this.mLogWriter.flush();
/* 359 */       this.mLogWriter.close();
/*     */     } catch (Exception e) {
/* 361 */       ZimbraLog.redolog.error("Error closing redo log " + this.mLogFile.getName(), e);
/*     */     }
/*     */     
/* 364 */     double rate = 0.0D;
/* 365 */     if (this.mCounter > 0) {
/* 366 */       rate = Math.round(this.mElapsed / this.mCounter * 1000.0D) / 1000.0D;
/*     */     }
/*     */     
/*     */ 
/* 370 */     ZimbraLog.redolog.info("Logged: " + this.mCounter + " items, " + rate + "ms/item");
/*     */   }
/*     */   
/*     */   public TransactionId getNewTxnId() {
/* 374 */     return this.mTxnIdGenerator.getNext();
/*     */   }
/*     */   
/*     */   public void log(RedoableOp op, boolean synchronous) {
/* 378 */     if ((!this.mEnabled) || (this.mRecoveryMode)) {
/* 379 */       return;
/*     */     }
/* 381 */     logOnly(op, synchronous);
/*     */     
/* 383 */     if (isRolloverNeeded(false)) {
/* 384 */       rollover(false, false);
/*     */     }
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */   public void commit(RedoableOp op)
/*     */   {
/* 392 */     if (this.mEnabled) {
/* 393 */       long redoSeq = this.mRolloverMgr.getCurrentSequence();
/* 394 */       CommitTxn commit = new CommitTxn(op);
/*     */       
/*     */ 
/*     */ 
/* 398 */       log(commit, false);
/* 399 */       commit.setSerializedByteArray(null);
/*     */     }
/*     */   }
/*     */   
/*     */   public void abort(RedoableOp op) {
/* 404 */     if (this.mEnabled) {
/* 405 */       AbortTxn abort = new AbortTxn(op);
/*     */       
/*     */ 
/* 408 */       log(abort, true);
/* 409 */       abort.setSerializedByteArray(null);
/*     */     }
/*     */   }
/*     */   
/*     */   public void flush() throws IOException {
/* 414 */     if (this.mEnabled) {
/* 415 */       this.mLogWriter.flush();
/*     */     }
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   protected void logOnly(RedoableOp op, boolean synchronous)
/*     */   {
/*     */     try
/*     */     {
/* 429 */       ReentrantReadWriteLock.ReadLock readLock = this.mRWLock.readLock();
/* 430 */       readLock.lockInterruptibly();
/*     */       try
/*     */       {
/* 433 */         synchronized (this.mActiveOps) {
/* 434 */           if (op.isStartMarker()) {
/* 435 */             this.mActiveOps.put(op.getTransactionId(), op);
/*     */           }
/* 437 */           if (op.isEndMarker()) {
/* 438 */             this.mActiveOps.remove(op.getTransactionId());
/*     */           }
/*     */         }
/*     */         try {
/* 442 */           long start = System.currentTimeMillis();
/* 443 */           this.mLogWriter.log(op, op.getInputStream(), synchronous);
/* 444 */           long elapsed = System.currentTimeMillis() - start;
/* 445 */           synchronized (this.mStatGuard) {
/* 446 */             this.mElapsed += elapsed;
/* 447 */             this.mCounter += 1;
/*     */           }
/*     */         } catch (NullPointerException e) {
/* 450 */           StackTraceElement[] stack = e.getStackTrace();
/* 451 */           if ((stack == null) || (stack.length == 0)) {
/* 452 */             ZimbraLog.redolog.warn("Caught NullPointerException during redo logging, but there is no stack trace in the exception.  If you are running Sun server VM, you could be hitting Java bug 4292742.  (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4292742)  Re-run the test case with client VM to see the stack trace.", e);
/*     */           }
/*     */           
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/* 483 */           signalFatalError(e);
/*     */         } catch (OutOfMemoryError e) {
/* 485 */           Zimbra.halt("out of memory", e);
/*     */         } catch (Throwable e) {
/* 487 */           ZimbraLog.redolog.error("Redo logging to logger " + this.mLogWriter.getClass().getName() + " failed", e);
/* 488 */           signalFatalError(e);
/*     */         }
/*     */         
/* 491 */         if (ZimbraLog.redolog.isDebugEnabled())
/* 492 */           ZimbraLog.redolog.debug(op.toString());
/*     */       } finally {
/* 494 */         readLock.unlock();
/*     */       }
/*     */     } catch (InterruptedException e) {
/* 497 */       synchronized (this.mShuttingDownGuard) {
/* 498 */         if (!this.mShuttingDown) {
/* 499 */           ZimbraLog.redolog.warn("InterruptedException while logging", e);
/*     */         } else {
/* 501 */           ZimbraLog.redolog.info("Thread interrupted for shutdown");
/*     */         }
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */ 
/*     */   private void checkpoint()
/*     */   {
/* 510 */     LinkedHashSet<TransactionId> txns = null;
/* 511 */     Iterator<Map.Entry<TransactionId, RedoableOp>> it; synchronized (this.mActiveOps) {
/* 512 */       if (this.mActiveOps.size() == 0) {
/* 513 */         return;
/*     */       }
/*     */       
/*     */ 
/* 517 */       txns = new LinkedHashSet();
/*     */       
/* 519 */       for (it = this.mActiveOps.entrySet().iterator(); it.hasNext();) {
/* 520 */         Map.Entry<TransactionId, RedoableOp> entry = (Map.Entry)it.next();
/* 521 */         txns.add(entry.getKey());
/*     */       }
/*     */     }
/* 524 */     Checkpoint ckpt = new Checkpoint(txns);
/* 525 */     logOnly(ckpt, true);
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   protected boolean isRolloverNeeded(boolean immediate)
/*     */   {
/* 536 */     boolean result = false;
/*     */     try {
/* 538 */       if (immediate) {
/* 539 */         result = !this.mLogWriter.isEmpty();
/*     */       } else {
/* 541 */         long size = this.mLogWriter.getSize();
/* 542 */         if (size >= this.mLogRolloverHardMaxBytes)
/*     */         {
/* 544 */           result = true;
/* 545 */         } else if ((size >= this.mLogRolloverSoftMaxBytes) && (size > this.mInitialLogSize))
/*     */         {
/* 547 */           long now = System.currentTimeMillis();
/* 548 */           long createTime = Math.min(this.mLogWriter.getCreateTime(), now);
/* 549 */           long age = now - createTime;
/* 550 */           result = age >= this.mLogRolloverMinAgeMillis;
/*     */         }
/*     */       }
/*     */     } catch (IOException e) {
/* 554 */       ZimbraLog.redolog.fatal("Unable to get redo log size");
/* 555 */       signalFatalError(e);
/*     */     }
/* 557 */     return result;
/*     */   }
/*     */   
/*     */   protected void setRolloverLimits(long minAgeMillis, long softMaxBytes, long hardMaxBytes) {
/* 561 */     this.mLogRolloverMinAgeMillis = minAgeMillis;
/* 562 */     this.mLogRolloverSoftMaxBytes = softMaxBytes;
/* 563 */     this.mLogRolloverHardMaxBytes = hardMaxBytes;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   protected File rollover(boolean force, boolean skipCheckpoint)
/*     */   {
/* 575 */     if (!this.mEnabled) {
/* 576 */       return null;
/*     */     }
/* 578 */     File rolledOverFile = null;
/*     */     
/*     */ 
/* 581 */     ReentrantReadWriteLock.WriteLock writeLock = this.mRWLock.writeLock();
/*     */     try {
/* 583 */       writeLock.lockInterruptibly();
/*     */     } catch (InterruptedException e) {
/* 585 */       synchronized (this.mShuttingDownGuard) {
/* 586 */         if (!this.mShuttingDown) {
/* 587 */           ZimbraLog.redolog.error("InterruptedException during log rollover", e);
/*     */         } else
/* 589 */           ZimbraLog.redolog.debug("Rollover interrupted during shutdown");
/*     */       }
/* 591 */       return rolledOverFile;
/*     */     }
/*     */     try
/*     */     {
/* 595 */       if (isRolloverNeeded(force)) {
/* 596 */         ZimbraLog.redolog.debug("Redo log rollover started");
/*     */         
/* 598 */         long start = System.currentTimeMillis();
/*     */         
/*     */ 
/* 601 */         Db.getInstance().flushToDisk();
/*     */         
/* 603 */         if (!skipCheckpoint)
/* 604 */           checkpoint();
/* 605 */         synchronized (this.mActiveOps) {
/* 606 */           rolledOverFile = this.mLogWriter.rollover(this.mActiveOps);
/* 607 */           this.mInitialLogSize = this.mLogWriter.getSize();
/*     */         }
/* 609 */         long elapsed = System.currentTimeMillis() - start;
/* 610 */         ZimbraLog.redolog.info("Redo log rollover took " + elapsed + "ms");
/*     */       }
/*     */     } catch (IOException e) {
/* 613 */       ZimbraLog.redolog.error("IOException during redo log rollover");
/* 614 */       signalFatalError(e);
/*     */     } finally {
/* 616 */       writeLock.unlock();
/*     */     }
/*     */     
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/* 631 */     return rolledOverFile;
/*     */   }
/*     */   
/*     */   public File forceRollover() {
/* 635 */     return forceRollover(false);
/*     */   }
/*     */   
/*     */   public File forceRollover(boolean skipCheckpoint) {
/* 639 */     return rollover(true, skipCheckpoint);
/*     */   }
/*     */   
/*     */   public RolloverManager getRolloverManager() {
/* 643 */     return this.mRolloverMgr;
/*     */   }
/*     */   
/*     */   public long getCurrentLogSequence() {
/* 647 */     return this.mRolloverMgr.getCurrentSequence();
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */   protected void resetActiveOps()
/*     */   {
/* 654 */     synchronized (this.mActiveOps) {
/* 655 */       this.mActiveOps.clear();
/*     */     }
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   protected ReentrantReadWriteLock.WriteLock acquireExclusiveLock()
/*     */     throws InterruptedException
/*     */   {
/* 673 */     ReentrantReadWriteLock.WriteLock writeLock = this.mRWLock.writeLock();
/* 674 */     writeLock.lockInterruptibly();
/* 675 */     return writeLock;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   protected void releaseExclusiveLock(ReentrantReadWriteLock.WriteLock exclusiveLock)
/*     */   {
/* 684 */     exclusiveLock.unlock();
/*     */   }
/*     */   
/*     */   protected void signalFatalError(Throwable e)
/*     */   {
/* 689 */     Zimbra.halt("Aborting process", e);
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */   public File[] getArchivedLogsFromSequence(long seq)
/*     */     throws IOException
/*     */   {
/* 698 */     return RolloverManager.getArchiveLogs(this.mArchiveDir, seq);
/*     */   }
/*     */   
/*     */   public File[] getArchivedLogs() throws IOException {
/* 702 */     return getArchivedLogsFromSequence(Long.MIN_VALUE);
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   public Pair<Set<Integer>, CommitId> getChangedMailboxesSince(CommitId cid)
/*     */     throws IOException, MailServiceException
/*     */   {
/* 716 */     Set<Integer> mailboxes = new HashSet();
/*     */     
/*     */ 
/* 719 */     ReentrantReadWriteLock.ReadLock readLock = this.mRWLock.readLock();
/*     */     try {
/* 721 */       readLock.lockInterruptibly();
/*     */     } catch (InterruptedException e) {
/* 723 */       synchronized (this.mShuttingDownGuard) {
/* 724 */         if (!this.mShuttingDown) {
/* 725 */           ZimbraLog.redolog.error("InterruptedException during redo log scan for CommitId", e);
/*     */         } else
/* 727 */           ZimbraLog.redolog.debug("Redo log scan for CommitId interrupted for shutdown");
/*     */       }
/* 729 */       return null;
/*     */     }
/*     */     
/* 732 */     File linkDir = null;
/*     */     try {
/*     */       File[] logs;
/*     */       try {
/* 736 */         long seq = cid.getRedoSeq();
/* 737 */         File[] archived = getArchivedLogsFromSequence(seq);
/* 738 */         if (archived != null) {
/* 739 */           File[] logs = new File[archived.length + 1];
/* 740 */           System.arraycopy(archived, 0, logs, 0, archived.length);
/* 741 */           logs[archived.length] = this.mLogFile;
/*     */         } else {
/* 743 */           logs = new File[] { this.mLogFile };
/*     */         }
/*     */         
/* 746 */         FileLogReader firstLog = new FileLogReader(logs[0]);
/* 747 */         if (firstLog.getHeader().getSequence() != seq)
/*     */         {
/* 749 */           throw MailServiceException.INVALID_COMMIT_ID(cid.toString());
/*     */         }
/*     */         
/*     */ 
/*     */ 
/* 754 */         String dirName = "tmp-scan-" + System.currentTimeMillis();
/* 755 */         linkDir = new File(this.mLogFile.getParentFile(), dirName);
/* 756 */         if (linkDir.exists()) {
/* 757 */           int suffix = 1;
/* 758 */           while (linkDir.exists()) {
/* 759 */             linkDir = new File(this.mLogFile.getParentFile(), dirName + "-" + suffix);
/*     */           }
/*     */         }
/* 762 */         if (!linkDir.mkdir())
/* 763 */           throw new IOException("Unable to create temp dir " + linkDir.getAbsolutePath());
/* 764 */         for (int i = 0; i < logs.length; i++) {
/* 765 */           File src = logs[i];
/* 766 */           File dest = new File(linkDir, logs[i].getName());
/* 767 */           IO.link(src.getAbsolutePath(), dest.getAbsolutePath());
/* 768 */           logs[i] = dest;
/*     */         }
/*     */       }
/*     */       finally {
/* 772 */         readLock.unlock();
/*     */       }
/*     */       
/*     */ 
/*     */ 
/* 777 */       long lastSeq = -1L;
/* 778 */       CommitTxn lastCommitTxn = null;
/* 779 */       boolean foundMarker = false;
/* 780 */       for (File logfile : logs) {
/* 781 */         FileLogReader logReader = new FileLogReader(logfile);
/* 782 */         logReader.open();
/* 783 */         lastSeq = logReader.getHeader().getSequence();
/*     */         try {
/* 785 */           RedoableOp op = null;
/* 786 */           while ((op = logReader.getNextOp()) != null) {
/* 787 */             if (ZimbraLog.redolog.isDebugEnabled())
/* 788 */               ZimbraLog.redolog.debug("Read: " + op);
/* 789 */             if ((op instanceof CommitTxn))
/*     */             {
/*     */ 
/* 792 */               lastCommitTxn = (CommitTxn)op;
/* 793 */               if (foundMarker) {
/* 794 */                 int mboxId = op.getMailboxId();
/* 795 */                 if (mboxId > 0) {
/* 796 */                   mailboxes.add(Integer.valueOf(mboxId));
/*     */                 }
/* 798 */               } else if (cid.matches(lastCommitTxn)) {
/* 799 */                 foundMarker = true;
/*     */               }
/*     */             }
/*     */           }
/* 803 */         } catch (IOException e) { ZimbraLog.redolog.warn("IOException while reading redolog file", e);
/*     */         } finally {
/* 805 */           logReader.close();
/*     */         }
/*     */       }
/* 808 */       if (!foundMarker)
/*     */       {
/* 810 */         throw MailServiceException.INVALID_COMMIT_ID(cid.toString());
/*     */       }
/* 812 */       CommitId lastCommitId = new CommitId(lastSeq, lastCommitTxn);
/* 813 */       return new Pair(mailboxes, lastCommitId);
/*     */     } finally {
/* 815 */       if (linkDir != null) {
/*     */         try
/*     */         {
/* 818 */           if (linkDir.exists())
/* 819 */             FileUtil.deleteDir(linkDir);
/*     */         } catch (IOException e) {
/* 821 */           ZimbraLog.redolog.warn("Unable to delete temporary directory " + linkDir.getAbsolutePath(), e);
/*     */         }
/*     */       }
/*     */     }
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/redolog/RedoLogManager.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */